//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

atcmd.cpp

Abstract:


Notes:


--*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author            Activity ID            Activity Headline
    2006-03-16          Wangxiaogang      CEDB00010042           W3K RIL Porting                
    2007-01-08          qinxuetao         CEDB00040729           Porting ril driver for CC/SMS
    2007-02-08          qinxuetao         CEDB200043651          Update ril driver for network and data service
    2007-03-09          qinxuetao         CEDB200046239          Update ril driver to enable AT command test through extended ATCI
    2007-04-06          gaorenhui         CEDB200050585          Add +CPI to indicate call progress information
    2007-04-26          qinxuetao         WM600002433            Add +TEDGE to get the access technology
    2007-05-10          qinxuetao         WM600002914            Delay Dial commands to wait for radio to settle
    2007-05-30          qinxuetao         WM600006448            Don't treate "+CME ERROR: 3" as USSD failure indication
    2007-06-07          qinxuetao         WM600003908            Can't enter STK main menu after bootup
    2007-06-21          qinxuetao         WM600007125            Change STK and PB init procedure
    2007-08-14          qinxuetao         WM600009403            Only init STK after some delay since phone on
    2007-09-07          zhongchuren     WM600016031            Fix report device.exe error when call 112 in phone off
    2007-12-26         Chenjianjun       WM600026083            When FDN is enabled,calling a number and it will stay the calling screen 
------------------------------------------------------------------------------
*/
#include "precomp.h"

//
// Globals
//

// Command Queue
CPriorityQueue<CCommand, 40>* g_pCmdQ = NULL;

// Response Queue
CQueue<CResponse, 10>* g_pRspQ = NULL;

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
// certain commands like COPS need to wait for the module to
// "settle" after it's been unlocked.
DWORD g_dwUnlocked;
#endif

#if defined(OEM1_GPRS_DEACT) || defined(EMP_DRIVER)
CRITICAL_SECTION g_csGPRSDeactLock;
bool g_fDeactNotificationRcvd = FALSE;
bool g_fDeactResponseRcvd = FALSE;
DWORD g_LastDeactNotificationRcvd = 0;
#endif

#ifdef RIL_RADIO_RESILIENCE
extern CRITICAL_SECTION g_csReboot;
#endif // RIL_RADIO_RESILIENCE
/*
#if defined(OEM1_CSQ_NOTIFICATION) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
extern RILSIGNALQUALITY g_rsq;
extern BOOL g_fSignalQualityReceived;
#endif //OEM2_DRIVER
*/
extern BOOL g_rgfCalltypeChecked[RIL_MAX_TRACKED_CALL_ID];
extern DWORD g_rgctCalltype[RIL_MAX_TRACKED_CALL_ID];

extern RILLOCATIONINFO g_rliLocationInfo;
extern BOOL g_fInitialLocationInfoReceived;
extern BOOL g_fLocationInfoReceived;
extern BOOL g_fSuppressRegStatusNotification;
extern BOOL g_fSuppressGPRSRegStatusNotification;

extern BOOL g_rfExternalCalltypeDetermination;

extern RINGINGCALLDATA g_rcdRingingCallData;
extern CRITICAL_SECTION g_csRingingCallData;

extern RILCALLINFO g_rgfCallStates[RIL_MAX_TRACKED_CALL_ID];
extern BOOL g_rgfCallsInProgress[RIL_MAX_TRACKED_CALL_ID];

//
// Place a command into the Command Queue
//
BOOL QueueCmdWithTimeout(CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, DWORD dwTimeout, DWORD dwRetries)
{
    FUNCTION_TRACE(QueueCmdWithTimeout);
    DEBUGCHK(0 != (dwOptions & CMDOPT_NOOP) || NULL != szCmd);

    CRilHandle* pRilDevice = pHandle->GetDevice();
    CCommand* pCmd = NULL;
    CNotificationData* pndNotify = pnd;
    APIINFO apiiInfo; memset(&apiiInfo,0,sizeof(apiiInfo)); // zero struct
    BOOL fRet = FALSE;

    DEBUGCHK(NULL != pRilDevice);

    // Get the info about this API
    pRilDevice->GetAPIInfo(apiid, apiiInfo);
    if (dwTimeout==0)
    {
        dwTimeout=apiiInfo.dwTimeout;
    }

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd || !pCmd->Init(pHandle, szCmd, szCmdPart2, dwOptions, apiiInfo.dwExecTime, dwTimeout, pndNotify, pfnParse, dwRetries, 0, 0, apiid, NULL, NULL))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdWithTimeout : Unable to construct or Init CCommand\r\n")));
        goto Error;
    }
    pndNotify = NULL;

    // Add this command to the handle's command list
    pHandle->GetCmdList()->Add(pCmd);

    rhrCmdID = pCmd->GetID();

    RETAILMSG(MSG_ON, (TEXT("RILDrv : t : Queueing command with ID: 0x%08x\r\n"), rhrCmdID));

#ifdef RIL_RADIO_RESILIENCE
        EnterCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ->Put(pCmd, INFINITE))
    {
#ifdef RIL_RADIO_RESILIENCE
        LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdWithTimeout : Unable to Put CCommand\r\n")));
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    // If this is a hangup command, and we're in data mode, we should drop out of data mode immediately
    // to handle this request
    if (pCmd->FHangup() && pRilDevice->GetComDevice()->FDataMode())
    {
        pRilDevice->SetDataEvent();
    }

    if (pCmd->FHangup() || pCmd->FDial() || pCmd->FAnswer())
    {
        SetEvent(g_hNewDialOrHangupEvent);
    }

    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
        delete pndNotify;
    }
    return fRet;
}

//
// Place a command into the Command Queue
//
BOOL QueueCmd(CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID)
{
    FUNCTION_TRACE(QueueCmd);
    return QueueCmdWithTimeout(pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0);
}


//
// Place a multipart command into the Command Queue
//
BOOL QueueMultipartCmd(CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID)
{
    FUNCTION_TRACE(QueueCmd);
    return QueueCmdWithTimeout(pHandle, szCmd, szCmdPart2, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0);
}


//
// Place a command into the Command Queue and ignore the response
//
BOOL QueueCmdIgnoreRsp(const APIID apiid, const LPCSTR szCmd, const DWORD dwOptions, DWORD dwTimeout, const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, DWORD dwRetries, DWORD dwRetriesOnError, DWORD dwRetryOnErrorDelay)
{
    FUNCTION_TRACE(QueueCmdIgnoreRsp);
    DEBUGCHK(0 != (dwOptions & CMDOPT_NOOP) || NULL != szCmd);

    CCommand* pCmd = NULL;
    BOOL fRet = FALSE;

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd || !pCmd->Init(NULL, szCmd, NULL, dwOptions | CMDOPT_IGNORERSP, EXECTIME_API_DEFAULT, dwTimeout, pnd, pfnParse, dwRetries, dwRetriesOnError, dwRetryOnErrorDelay, apiid, NULL, NULL))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdIgnoreRsp : Unable to construct or Init CCommand\r\n")));
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    EnterCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ->Put(pCmd, INFINITE))
    {
#ifdef RIL_RADIO_RESILIENCE
        LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdIgnoreRsp : Unable to Put CCommand\r\n")));
        goto Error;
    }
#ifdef RIL_RADIO_RESILIENCE
    LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE
    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
    }
    return fRet;
}

//
// Place a command into the Command Queue and ignore the response
//
BOOL QueueCmdIgnoreRspWithData(const APIID apiid, const LPCSTR szCmd, const DWORD dwOptions, DWORD dwTimeout, const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, DWORD dwRetries, DWORD dwRetriesOnError, DWORD dwRetryOnErrorDelay, const PFN_CMD_PARSE_DATA pfnParseWithData, const PVOID pParseData)
{
    FUNCTION_TRACE(QueueCmdIgnoreRsp);
    DEBUGCHK(0 != (dwOptions & CMDOPT_NOOP) || NULL != szCmd);

    CCommand* pCmd = NULL;
    BOOL fRet = FALSE;

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd || !pCmd->Init(NULL, szCmd, NULL, dwOptions | CMDOPT_IGNORERSP, EXECTIME_API_DEFAULT, dwTimeout, pnd, NULL, dwRetries, dwRetriesOnError, dwRetryOnErrorDelay, apiid, pfnParseWithData, pParseData))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdIgnoreRsp : Unable to construct or Init CCommand\r\n")));
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    EnterCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ->Put(pCmd, INFINITE))
    {
#ifdef RIL_RADIO_RESILIENCE
        LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueCmdIgnoreRsp : Unable to Put CCommand\r\n")));
        goto Error;
    }
#ifdef RIL_RADIO_RESILIENCE
    LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE
    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
    }
    return fRet;
}
//
//
//
DWORD WINAPI RequeueThreadProc(LPVOID lpParameter)
{
    FUNCTION_TRACE(RequeueThreadProc);
    REQUEUE_THREAD_DATA* prtd = (REQUEUE_THREAD_DATA*)lpParameter;
    DEBUGCHK(NULL != prtd);
    DEBUGCHK(NULL != prtd->pCmd);

    Sleep(prtd->dwDelay);

#ifdef RIL_RADIO_RESILIENCE
    EnterCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ->Put(prtd->pCmd, INFINITE))
    {
        delete prtd->pCmd;
    }

#ifdef RIL_RADIO_RESILIENCE
    LeaveCriticalSection(&g_csReboot);
#endif // RIL_RADIO_RESILIENCE

    prtd->pCmd = NULL;
    delete prtd;
    return 0;
}


//
//
//
BOOL RequeueCmdWithDelay(CCommand* pCmd, DWORD dwDelay)
{
    FUNCTION_TRACE(RequeueCmdWithDelay);
    DEBUGCHK(NULL != pCmd);
    DEBUGCHK(TRUE == pCmd->FIgnoreRsp());

    REQUEUE_THREAD_DATA* prtd = NULL;
    CCommand* pCmdNew = NULL;
    DWORD dwThreadID;
    BOOL fRet = FALSE;

    // Allocate parameter structure to be passed to the thread proc
    prtd = new REQUEUE_THREAD_DATA;
    if (!prtd)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RequeueCmdWithDelay : Unable to construct REQUEUE_THREAD_DATA\r\n")));
        goto Error;
    }

    // Set up a command to be sent
    pCmdNew = new CCommand;
    if (!pCmdNew || !pCmdNew->Init(pCmd))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RequeueCmdWithDelay : Unable to construct or Init CCommand\r\n")));
        goto Error;
    }

    prtd->pCmd = pCmdNew;
    prtd->dwDelay = dwDelay;
    if (!CreateThread(NULL, 0, RequeueThreadProc, (LPVOID)prtd, 0, &dwThreadID))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RequeueCmdWithDelay : Unable to CreateThread(RequeueThreadProc)\r\n")));
        goto Error;
    }
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete prtd;
        delete pCmd;
    }
    return fRet;
}

//
//
//
BOOL QueueRsp(CRilHandle* pRilDevice, CResponse*& rpRsp)
{
    FUNCTION_TRACE(QueueRsp);
    BOOL fRet = FALSE;

    // Remember that we're not waiting for AT response anymore
    pRilDevice->StopWaitingForRsp();

    // Queue the command response
    if (!g_pRspQ->Put(rpRsp, INFINITE))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : QueueRsp : Unable to Put CResponse\r\n")));
        goto Error;
    }
    rpRsp = NULL;

    fRet = TRUE;

    Error:
    return fRet;
}

BOOL ParseCLCCNotification(CRilHandle *pRilDevice, CCommand* pCmd, CResponse* pRsp)
{
    FUNCTION_TRACE(ParseCLCCNotification);
    BOOL Found=FALSE;
    LPCSTR szRsp;
    UINT nId;
    UINT nDirection;
    UINT nStatus;
    UINT nType;
    UINT nMultiParty;

    szRsp = pRsp->GetData();

    // Parse "+CLCC: "
    while (MatchStringAnywhere(szRsp,"+CLCC: ",szRsp))
    {
        Found=TRUE;

        // Parse "<id>"
        if (!ParseUInt(szRsp, TRUE, nId, szRsp))
        {
            continue;
        }

        // Parse ",<direction>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nDirection, szRsp))
        {
            continue;
        }

        // Parse ",<status>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nStatus, szRsp))
        {
            continue;
        }

        // Parse ",<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nType, szRsp))
        {
            continue;
        }

        // Parse ",<multiparty>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nMultiParty, szRsp))
        {
            continue;
        }

        if (nStatus==2)
        {
            RILDIALINFO rdi;
            rdi.cbSize = sizeof(rdi);
            rdi.dwParams = RIL_PARAM_DI_ALL;
            rdi.hrCmdId = pCmd->GetID();
            rdi.dwCallId = nId;
            (void)pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_DIAL,
                                                &rdi, sizeof(RILDIALINFO));
            break;
        }
    }

    return Found;
}

//
// Thread responsible for sending commands from the Command Queue to COM port
//
DWORD WINAPI CmdThreadProc(LPVOID lpParameter)
{
    FUNCTION_TRACE(CmdThreadProc);
    DEBUGCHK(lpParameter != NULL);
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;
    RETAILMSG(MSG_ON, (TEXT("RILDrv : t : CmdThreadProc : Entering, pRilDevice=0x%x\r\n"),pRilDevice));
    const DWORD dwReturn = pRilDevice->CommandThread();
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CmdThreadProc : THREAD IS EXITING, dwReturn=0x%x\r\n"),dwReturn));
    return dwReturn;
}

//
// Thread responsible for reading responses from COM port into the Response Queue
//
DWORD WINAPI ReadThreadProc(LPVOID lpParameter)
{
    FUNCTION_TRACE(ReadThreadProc);
    DEBUGCHK(lpParameter != NULL);
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;

    // Switch the thread into higher priority (to guarantee that the module's in buffer doesn't get overflown)
    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : ReadThreadProc : Unable to raise priority of read thread!!\r\n")));
        return 0;
    }

    RETAILMSG(MSG_ON, (TEXT("RILDrv : t : ReadThreadProc : Entering, pRilDevice=0x%x\r\n"),pRilDevice));
    const DWORD dwReturn = pRilDevice->ResponseThread();
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : ReadThreadProc : THREAD IS EXITING, dwReturn=0x%x\r\n"),dwReturn));
    return dwReturn;
}

DWORD CRilHandle::CommandThread()
{
    FUNCTION_TRACE(CRilHandle::CommandThread);
    CCommand* pCmd = NULL;
    BOOL fBackToCmdMode = FALSE;
    DWORD dwReferenceTime;
    BOOL fDummy;
    HRESULT hr;

    DEBUGCHK(m_pComDevice != NULL);

    // Tell the main thread that we've reached the checkpoint
    GetCheckPoint()->Reached();

#if defined(OEM1_DRIVER) && !defined(OEM2_DRIVER) && !defined(EMP_DRIVER)
    // On this hardware, we need to wait for the radio to send us it's wakeup string before we start
    // sending it commands.
    if(!m_pComDevice->WaitForRadioSignon(this))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::CommandThread : WaitForRadioSignon failed\r\n")));
    }
#endif // OEM1_DRIVER

    while (1)
    {
        // Make sure the module is prepared to handle the command before sending it (except for init commands)
        if (!WaitForCommandOrCancel())
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::CommandThread : Cancel was set\r\n")));
            goto Error;
        }

        // Get the next command from the Queue
        hr = g_pCmdQ->Get(pCmd, INFINITE);
        if (RIL_E_CANCELLED == hr)
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::CommandThread : Get returned RIL_E_CANCELLED\r\n")));
            goto Error;
        }
        DEBUGCHK(FALSE == FAILED(hr));

        // If we failed initialization, ignore any non-init commands in the queue
        if (m_fFailedInit && !pCmd->FInit())
        {
            HRESULT dwError = RIL_E_RADIOFAILEDINIT;
            pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));
            continue;
        }

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
        if (g_dwUnlocked)
        {
            DWORD dwTimeSinceUnlock = GetTickCount() - g_dwUnlocked;
            
// this number was picked by guesswork/experimentation
// for dragonfly, this number >= 1000
#define WAIT_FOR_MODULE_TO_SETTLE 2000
            if (dwTimeSinceUnlock < WAIT_FOR_MODULE_TO_SETTLE)
            {
                // certain commands like COPS need to wait for the module to
                // "settle" after it's been unlocked.
                Sleep(WAIT_FOR_MODULE_TO_SETTLE - dwTimeSinceUnlock);
            }
            g_dwUnlocked = 0;
        }
#endif
/*
#ifdef WAVECOM_DRIVER
#define WAIT_FOR_RADIO_TO_SETTLE    (20000UL)
#define WAIT_FOR_RADIO_WHILE_NO_SIM (60000UL)
        if( (APIID_DIAL == pCmd->GetAPIID()) && g_dwPhoneOn )
        {
            CCommand* pNextCmd = NULL;
            DWORD dwWaitTimeNeeded = (g_fNoSIM)?(WAIT_FOR_RADIO_WHILE_NO_SIM):(WAIT_FOR_RADIO_TO_SETTLE);
            DWORD dwTimeSincePhoneOn = ( GetTickCount() - g_dwPhoneOn );
            RETAILMSG(MSG_ON, (TEXT("Need to wait %d ms, and already %d ms elapsed\r\n"), dwWaitTimeNeeded, dwTimeSincePhoneOn));
            while( dwTimeSincePhoneOn < dwWaitTimeNeeded )
            {
                if(SUCCEEDED(g_pCmdQ->Get(pNextCmd, dwWaitTimeNeeded - dwTimeSincePhoneOn)))
                {
                    g_pCmdQ->Put(pCmd, INFINITE);
                    pCmd = pNextCmd;
                }
                if( APIID_DIAL==pCmd->GetAPIID())
                {
                    RETAILMSG(MSG_ON,(TEXT("Delay Dial commands\r\n")));
                    Sleep(200);
                    dwTimeSincePhoneOn = ( GetTickCount() - g_dwPhoneOn );
                    continue;
                }
                else if( APIID_GETCALLLIST==pCmd->GetAPIID())
                {
                    RETAILMSG(MSG_ON,(TEXT("Delay GetCallList commands\r\n")));
                    Sleep(200);
                    continue;
                }
                else
                {
                    RETAILMSG(MSG_ON,(TEXT("Send other commands\r\n")));
                    break;
                }
            }
            if(APIID_DIAL == pCmd->GetAPIID())
            {
                g_dwPhoneOn = 0;
            }
        }
#endif
*/
        // Send the command to comm port
        if (!m_pComDevice->SendRILCmdHandleRsp(this, pCmd, fDummy, fDummy))
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::CommandThread : SendRILCmdHandleRsp failed\r\n")));
            // Don't give up and exit, just try again
            continue;
        }
        DEBUGCHK(NULL == pCmd);

        // If this command dropped the modem into the data mode, switch to data-mode operation
        if (m_pComDevice->FDataMode())
        {
            fBackToCmdMode = FALSE;
            dwReferenceTime = GetTickCount();
            ResetEvent(m_hDataEvent);

            while (!fBackToCmdMode)
            {
                BOOL fSentCommands = FALSE;

                // Sleep for a timeout
                WaitForSingleObject(m_hDataEvent, GetDataModeCmdDelay());

                // If there is a pending command in the queue, send it through the VSP backdoor
                if (!g_pCmdQ->FEmpty() &&
                    m_pComDevice->SendRILCmdsInDataMode(this, fBackToCmdMode))
                {
                    fSentCommands = TRUE;
                }

                // See if there's a command pending
                if (!fSentCommands &&
                    GetMaxDataModeTimeWithoutCmd() < GetTickCount() - dwReferenceTime)
                {
                    // The command queue is empty and we haven't sent a command for too long -- queue a no-op command
                    (void)QueueCmdIgnoreRsp(APIID_NONE, "AT\r", CMDOPT_NONE, g_TimeoutCmdNoOp,NULL, NULL,0,0,0);

                    (void)m_pComDevice->SendRILCmdsInDataMode(this, fBackToCmdMode);

                    fSentCommands = TRUE;
                }

                if (fSentCommands)
                {
                    // Update the reference time
                    dwReferenceTime = GetTickCount();
                }

                if (fBackToCmdMode || FCancelSet())
                {
                    break;
                }
#ifndef NO_RLSD_SUPPORT
                DWORD dwStatus;
                if (m_pComDevice->VirtGetCommModemStatus(&dwStatus) && !(dwStatus & MS_RLSD_ON))
                {
                    // RLSD is OFF, which means that data connection has been dropped
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::CommandThread : Detected remote disconnect due to loss of RLSD\r\n")));

                    // Exit out of the data mode
                    (void)m_pComDevice->ExitDataMode();

                    // Notify all clients
                    (void)BroadcastRealBlobNotification(RIL_NOTIFY_CALLSTATECHANGED, NULL, 0);
                    fBackToCmdMode = TRUE;
                }
#endif
            }
        }
    }

    Error:
    delete pCmd;
    g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_COMMANDTHREADEXIT);
    return 0;
}
static BOOL IsNocarrierExpectedCmd(CCommand* pCommand)
{
    BOOL fRet = FALSE;

    if((pCommand!=NULL)&& 
	(APIID_DIAL == pCommand->GetAPIID()||(APIID_ENTERGPRSDATAMODE == pCommand->GetAPIID())))/*&&
	(strstr(pCommand->GetCmd(),";\r") == NULL))*/
    {
        RETAILMSG(1, (TEXT("if connecting to network by ATD COMMAND  the No carrier will be as response to ATD\r\n")));
        fRet = TRUE;
    }
    else
    {
        RETAILMSG(1, (TEXT("if making Voice call  by ATD COMMAND the No carrier will not be as response to ATD\r\n")));
    }
    return fRet;
}
BOOL CRilHandle::HandleRxData(char *szData, DWORD dwRead, bool fDataOnNotificationComPort)
{
    LPSTR szAppend = NULL;
    LPSTR szRemainder = NULL;
    UINT cbAppend;
    UINT cbRemainder;
    void* pBlob;
    UINT cbBlob;
    CResponse* pRsp = NULL;
    BOOL bSuccess=FALSE;

    // DEBUGMSG(ZONE_INFO, (TEXT("RILDrv : i : ResponseThread read %s\r\n"), TString(PrintableString(szData, dwRead))));

    if (!AppendReadBytes(szData, dwRead, fDataOnNotificationComPort))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : AppendReadBytes failed\r\n")));
        goto Exit;
    }

    while (GiveUpReadBytes(szAppend, cbAppend, fDataOnNotificationComPort))
    {
        if (!cbAppend)
        {
            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            break;
        }

        if (!pRsp)
        {
            // Allocate a new response, if we need it
            pRsp = new CResponse;
            if (!pRsp)
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Unable to construct CResponse\r\n")));
                // Critically low on memory
                SignalCriticalError(RILLOG_EVENT_LOWMEMORY, __LINE__, __FILE__);
                goto Exit;
            }
        }

        if (m_fLastCommandTimedOut)
        {
            pRsp->SetPotentialBogusResponseFlag();
            m_fLastCommandTimedOut = FALSE;
        }

        if (!pRsp->AppendString(szAppend, cbAppend, szRemainder, cbRemainder, fDataOnNotificationComPort))
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : AppendString failed\r\n")));
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY, __LINE__, __FILE__);
            goto Exit;
        }

        // Does this contain a complete response?
        if (!szRemainder)
        {
            // No -- break out of this loop and wait for more data to come

            // Move the data back to the Read Bytes buffer
            if (!InheritReadBytes(pRsp, fDataOnNotificationComPort))
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : InheritReadBytes failed\r\n")));
                // Critically low on memory
                SignalCriticalError(RILLOG_EVENT_LOWMEMORY, __LINE__, __FILE__);
                goto Exit;
            }

            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            cbAppend = 0;
            break;
        }
        else
        {
            // We have complete response. Now is a good time to pass the response on to the logging application if one exists.
            LogATCommand(pRsp->GetData(), pRsp->GetLength(), ATCMD_LOG_RESPONSE);

#ifdef RIL_WATSON_REPORT
            // If the radio was previously hung, Watson logging was reported once and disabled to 
            // prevent duplicate logs reported when the radio hangs.
            // We are receiving data from the radio, so make sure the Watson called flag is reset
            // so subsequent problems will be reported.
            g_fWatsonCalled = FALSE;
#endif // RIL_WATSON_REPORT

#if defined(OEM1_DRIVER) || defined(OEM2_SIMTOOLKIT) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
            if (g_CachedToolKitNotifs[0] && IsAPIReady(SH_SHELL) && IsSTKReady() )
            {
                UINT i;
                
                // shell's started.  safe to send toolkit notifications now.
                for (i = 0; (i < MAX_TOOLKITNOTIFS) && (g_CachedToolKitNotifs[i]); i++)
                {
                    CResponse *pRspCached = g_CachedToolKitNotifs[i];
                    void* pBlobCached;
                    UINT cbBlobCached;
                    pRspCached->GetBlob(pBlobCached, cbBlobCached);
                    (void)BroadcastRealBlobNotification(pRspCached->GetNotifyCode(), pBlobCached, cbBlobCached);
                    pRspCached->DeleteBlob();
                    pBlobCached = NULL;
                    cbBlobCached = 0;
                    delete pRspCached;
                    g_CachedToolKitNotifs[i] = NULL;
                }
            }
#endif

            if ( g_cphschange )
            {
                /* let upper layer know about CPHS */
                BroadcastRealBlobNotification( RIL_NOTIFY_REGSTATUSCHANGED, &g_dwRegStatus, sizeof(g_dwRegStatus) );

                g_cphschange = FALSE;
            }

#if defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
            if (g_fSystemChanged)
            {
                // Notify that the system and its capability are changed
                RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Sending RIL_NOTIFY_SYSTEMCHANGED and RIL_NOTIFY_SYSTEMCAPSCHANGED\r\n")));
                (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCHANGED, g_dwSystemType);
                (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCAPSCHANGED, g_dwSystemCaps);
                g_fSystemChanged = FALSE;
            }
#endif

#ifdef RIL_ENABLE_EONS
            if ( g_eonschange )
            {
                /* let upper layer know about EONS */
                BroadcastRealBlobNotification( RIL_NOTIFY_EONS, NULL, 0 );

                g_eonschange = FALSE;
            }
#endif

            if (pRsp->FUnrecognized())
            {
                // We couldn't recognize this response -- assume it was corrupted and throw it away
#ifdef DEBUG
                DEBUGMSG(ZONE_INFO, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Ignoring an unrecognized response: %d chars of %s\r\n"),
                           pRsp->GetLength(), VerboseString(TString(pRsp->GetData()))));
#endif // DEBUG
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_UNRECOGNIZEDRSP, PrintableString(pRsp->GetData(), pRsp->GetLength()));

                delete pRsp;
                pRsp = NULL;

            }
            else if (!pRsp->FUnsolicited() || 
                     (RIL_RESULT_BUSY == pRsp->GetNotifyCode() && FWaitingForRsp() && (NULL != m_pCurrCommand && m_pCurrCommand->FDial()) ))
            {
#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER)  || defined(EMP_DRIVER)
                if (!pRsp->GetNotifyCode() && m_fInited)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Module reset itself?!, rebooting via CPM\r\n")));
                    if(!CreateProcess(TEXT("rs.exe"), NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL))
                    {
                        SignalCriticalError(RILLOG_EVENT_TOOMANYTIMEOUTS, __LINE__, __FILE__);
                    }
                }
#endif
                //
                // This is a command result
                //
                // Is someone waiting for this command result?
#if defined(OEM2_DRIVER)
                // On TTPCOM, some CPINN command responses come from notification COM port
                if (FWaitingForRsp() && ((FALSE == fDataOnNotificationComPort) || pRsp->FCPINNRsp()))
#else
                if (FWaitingForRsp() && (FALSE == fDataOnNotificationComPort) && (RIL_RESULT_NOCARRIER != pRsp->GetNotifyCode() || IsNocarrierExpectedCmd(m_pCurrCommand)))
#endif
                {
#if 0 // NOT USED
                    // Treat +CLCC responses while waiting for dial as notifications
                    if ((m_pCurrCommand->FDial()) && (ParseCLCCNotification(this, m_pCurrCommand, pRsp)))
                    {
                        delete pRsp;
                        pRsp = NULL;
                    }
                    else
#endif // 0
#ifdef RIL_RADIO_RESILIENCE
                    if ( m_pCurrCommand && (RIL_RESULT_OK != pRsp->GetNotifyCode()) && (m_pCurrCommand->CmdOptIsSet(CMDOPT_CPINENTERED)) )
                    {
                        // Someone just tried to set the CPIN and failed.  Flush the CPIN cache so we don't auto-unlock the phone or PUK the SIM.
                        SavePINSecure("");
                    }

                    if (RIL_RESULT_ERROR == pRsp->GetNotifyCode())
                    {
                        UINT cbSize = 0;
                        LPVOID pBlob = NULL;
                        pRsp->GetBlob(pBlob, cbSize);
                        if((sizeof(HRESULT) == cbSize) && (RIL_E_RADIOREBOOTED == *(HRESULT *)pBlob))
                        {
                            // Reset g_rcdRingingCallData.fCalltypeValid
                            g_rcdRingingCallData.fCalltypeValid = FALSE;

                            // The command failed because the radio rebooted.
                            // Send up a disconnect notification just in case.
                            (void)m_pComDevice->ExitDataMode();
                            (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT,
                                                                 RIL_DISCINIT_REMOTE);
                        }
                    }
#endif // RIL_RADIO_RESILIENCE

                    // Yes -- place it into the Response Q

#if defined(OEM1_GPRS_DEACT) || defined(EMP_DRIVER)
                     bool fQueueResponse = TRUE;
                     // The current command is a GPRS deactivation command
                     if (m_pCurrCommand && m_pCurrCommand->CmdOptIsSet(CMDOPT_DEACT)) {
#ifdef EMP_DRIVER
                        // EMP radio may return a 3<cr>0<cr> response for the GPRS deactivation command.
                        // We need to discard 3<cr>, and only return an OK response
                        if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode())
                        {
                            fQueueResponse = FALSE;
                            (VOID) pRsp->UpdateDataPointer((UINT)strlen("3\r"));
                        }
#else
                        // This is a ME DEACT notification (note that only a ME DEACT notification maps to a solicited
                        // response with a dwCode of RIL_NOTIFY_GPRS_DISCONNECT)
                        EnterCriticalSection(&g_csGPRSDeactLock); 
                        if (RIL_NOTIFY_GPRS_DISCONNECT == pRsp->GetNotifyCode()) {
                            // The command response has already been received, convert the dwCode to look like a result
                            // and make this as the command response, reset all the flags as we are done
                            if (g_fDeactResponseRcvd) {
                                // AT+CGACT, 0, ME case - COMPLETE HERE
                                pRsp->MakeOK();
                                g_fDeactNotificationRcvd = g_fDeactResponseRcvd = FALSE;
                                }
                            // Otherwise, do not queue this response as we have to wait for the actual command response
                            else {
                                // AT+CGACT, ME, 0 case - INCOMPLETE HERE
                                fQueueResponse = FALSE;
                                (VOID) pRsp->UpdateDataPointer((UINT)strlen("4\r"));
                                }
                            }
                        // This is the response for the deactivation command
                        else {
                            g_fDeactResponseRcvd = TRUE;
                            // The ME DEACT notification has already been received
                            if (g_fDeactNotificationRcvd) {
                                // The ME DEACT notification is stale, wait for a new one, do not queue this response
                                // Note the calculation below is rollover safe but it will not work if the MEDEACT is as stale
                                // as 49 days - the assumption is we will not have such a stale ME DEACT under normal
                                // usage
                                if ((GetTickCount() - g_LastDeactNotificationRcvd) > STALE_MEDEACT_THRESHOLD) {
                                    // Stale ME, AT+CGACT, 0 case (becomes AT+CGACT, 0, ME case) - INCOMPLETE HERE
                                    g_fDeactNotificationRcvd = FALSE;
                                    fQueueResponse = FALSE;                                    
                                    }
                                // The ME DEACT notification is not stale, this response becomes the real response
                                else {
                                    // Fresh ME, AT+CGACT, 0 case - COMPLETE HERE
                                    g_fDeactNotificationRcvd = g_fDeactResponseRcvd = FALSE;
                                    }
                                }
                            // Otherwise, wait for a ME DEACT, do not queue this response
                            else {
                                // AT+CGACT, 0, ME case - INCOMPLETE HERE
                                fQueueResponse = FALSE;
                                (VOID) pRsp->UpdateDataPointer((UINT)strlen("0\r"));
                                }
                            }
                        LeaveCriticalSection(&g_csGPRSDeactLock);  
#endif // EMP_DRIVER
                        }
                        if (fQueueResponse) {
#endif
                            if (!QueueRsp(this, pRsp))
                            {
                                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : QueueRsp failed\r\n")));
                                goto Exit;
                            }
                            DEBUGCHK(NULL == pRsp);

#if defined(OEM1_GPRS_DEACT) || defined(EMP_DRIVER)
                            }
#endif
                }
                else
                {
                    // No -- see if it's one of the exceptions, and if not, throw it away
                    if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode())
                    {
                        // This is an unsolicited NOCARRIER, the remote end must have hung up -- exit
                        //    out of the data mode and send out a notification
                        (void)m_pComDevice->ExitDataMode();

                        // Reset g_rcdRingingCallData.fCalltypeValid
                        g_rcdRingingCallData.fCalltypeValid = FALSE;

                        // For OEM1, ignore unsolicited NOCARRIER. OEM1 implements call progress notifications
                        // which is the preferred way to get status of the call (including remote side hanging up)
#if !defined(OEM1_GPRS_DEACT) && !defined(EMP_DRIVER) && !defined(WAVECOM_DRIVER)   /* add code by gaorenhui for Don't send NOCARRIER for WAVECOM_DRIVER for CEDB200050585. */
                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT,
                                                             RIL_DISCINIT_REMOTE);
                        RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Detected remote disconnect (AT)\r\n")));
#endif
                        // Indicate call is inactive to audio driver only if call list is empty.
                        IndicateCallActivityToAudioSubsystem ( FALSE, TRUE );

#ifdef WAVECOM_DRIVER
                        // HW-SPECIFIC: WaveCom hardware sends unsolicited "+CME ERROR: 13" when it can't
                        //              detect a SIM card
                    }
                    else if (RIL_E_SIMFAILURE == pRsp->GetError())
                    {
                        // This is an unsolicited "+CME ERROR: 13" -- send out a "SIM isn't accessible"
                        //    notification
                        (void)BroadcastRealBlobNotification(RIL_NOTIFY_SIMNOTACCESSIBLE, NULL, 0);
#endif // WAVECOM_DRIVER
#if 0
#ifdef WAVECOM_DRIVER
                        // HW-SPECIFIC: WaveCom hardware sends unsolicited "+CME ERROR: 3" after a rejected
                        //              USSD transaction
                    }
                    else if (RIL_E_OPNOTALLOWED == pRsp->GetError())
                    {
                        // This is an unsolicited "+CME ERROR: 3" -- send out a "USSD error" notification
                        RILSUPSERVICEDATA rssd;
                        rssd.cbSize = sizeof(RILSUPSERVICEDATA);
                        rssd.dwStatus = RIL_SUPSVCDATASTATUS_ERROR;
                        rssd.dwParams = RIL_PARAM_SSDI_STATUS;
                        (void)BroadcastRealBlobNotification(RIL_NOTIFY_SUPSERVICEDATA,
                                                            &rssd, sizeof(RILSUPSERVICEDATA));
#endif // WAVECOM_DRIVER
#endif
                    }
                    else
                    {
#ifdef DEBUG
                        DEBUGMSG(ZONE_INFO, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Ignoring a response no one is waiting for: %d chars of %s\r\n"),
                                   pRsp->GetLength(), VerboseString(TString(pRsp->GetData()))));
#endif // DEBUG
                        g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_NOONEWAITING, PrintableString(pRsp->GetData(), pRsp->GetLength()));
                    }
                    delete pRsp;
                    pRsp = NULL;
                }
            }
            else
            {
                if (RIL_RESULT_BUSY == pRsp->GetNotifyCode())
                {
                    // Reset g_rcdRingingCallData.fCalltypeValid
                    g_rcdRingingCallData.fCalltypeValid = FALSE;

                    (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_BUSY);
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Detected busy signal\r\n")));
                }
                else
                {
                    // This is an unsolicited notification -- broadcast it to all clients
                    // (with the exceptions :
                    //     CONNECT's received while in data mode, because they are effectively
                    //         internal to RIL/VSP
                    //     Notify Codes of NULL, which means the parser is declining to send a
                    //         properly parsed radio notification

                    // Make sure the registration actually changed (rather than the location data only) before
                    //  sending up a RIL_NOTIFY_REGSTATUSCHANGED.
                    if ((!((RIL_NOTIFY_REGSTATUSCHANGED == pRsp->GetNotifyCode()) && g_fSuppressRegStatusNotification)) &&
                        (!((RIL_NOTIFY_GPRSREGSTATUSCHANGED == pRsp->GetNotifyCode()) && g_fSuppressGPRSRegStatusNotification)))
                    {
                        if ((!m_pComDevice->FDataMode() || RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode()) &&
                            (NULL != pRsp->GetNotifyCode()))
                        {
                            pRsp->GetBlob(pBlob, cbBlob);
                            (void)BroadcastRealBlobNotification(pRsp->GetNotifyCode(), pBlob, cbBlob);
                            pRsp->DeleteBlob();
                            pBlob = NULL;
                            cbBlob = 0;
#ifdef RIL_ENABLE_AT_CHANNEL_FOR_ATCI
                            //Send unsolicited AT commands to ATCI
                            if(g_dwATCI && !g_fATCILogOn)
                            {
                                CNotificationData* pnd = NULL;
                                pnd = new CNotificationData;
                                if (pnd && pnd->InitFromRealBlob(RIL_NOTIFY_UNSOLICITED_ATCMD, pRsp->GetData(), pRsp->GetLength()))
                                {
                                    NotifyHandle((PVOID)g_dwATCI, (DWORD)pnd);
                                }
                                delete pnd;
                                pnd = NULL;
                            }
#endif
                        }
                    }

                    // See if the last command caused us to enter (or return) to data mode
                    if (RIL_NOTIFY_CONNECT == pRsp->GetNotifyCode())
                    {
                        // Yes -- let the virtual serial driver know
                        if (m_pComDevice->EnterDataMode(szRemainder, cbRemainder))
                        {
                            szRemainder += cbRemainder;
                            cbRemainder = 0;

                            // Place a response to command that caused connection into the Response Queue
                            //    (the response will later be changed to OK)
                            if (!QueueRsp(this, pRsp))
                            {
                                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : QueueRsp failed\r\n")));
                                goto Exit;
                            }
                            DEBUGCHK(NULL == pRsp);

                            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Waiting for command mode\r\n")));
                            // Wait until module drops out of data mode
                            if (!m_pComDevice->WaitForCommandMode())
                            {
                                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : WaitForCommandMode failed\r\n")));
                                goto Exit;
                            }
                            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : Done waiting for command mode\r\n")));
                        }
                    }
#if defined(OEM1_CSQ_NOTIFICATION) || defined(OEM2_DRIVER)  || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
                    else if (RIL_NOTIFY_REGSTATUSCHANGED == pRsp->GetNotifyCode() && g_fSignalQualityReceived)
                    {
                        // Throw up a signal quality notification for good measure.
                        BroadcastRealBlobNotification(RIL_NOTIFY_SIGNALQUALITY, &g_rsq, sizeof(g_rsq));
                    }
#endif

                }
                delete pRsp;
                pRsp = NULL;
            }

            // Throw up a location update notification if required.
            if (g_fLocationInfoReceived)
            {
                BroadcastRealBlobNotification(RIL_NOTIFY_LOCATIONUPDATE, &g_rliLocationInfo, sizeof(g_rliLocationInfo));
                g_fLocationInfoReceived = FALSE;
            }

            // perform external calltype determination processing
            if (g_rfExternalCalltypeDetermination)
            {
                // Throw up a ring notification if required.  This is only required after a +CLIP notification is the first
                // notification to provide a reliable calltype after an initial CRING notification has been suppressed.
                // This is perfromed here to to stay consistent with the way the RIL handles other secondary or
                // after-the-fact notifications (e.g. SIM Toolkit notifications) that are sent along with primary notifications.
                EnterCriticalSection(&g_csRingingCallData);
                if (g_rcdRingingCallData.fForceRingNotification)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Throwing delayed ring notification.\r\n")));
                    RILRINGINFO rri; memset(&rri,0,sizeof(rri));
                    rri.cbSize = sizeof(RILRINGINFO);
                    rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
                    rri.dwCallType = g_rcdRingingCallData.dwCalltype;
                    g_rcdRingingCallData.fForceRingNotification = FALSE;
                    LeaveCriticalSection(&g_csRingingCallData);
                    BroadcastRealBlobNotification(RIL_NOTIFY_RING, &rri, sizeof(rri));
                }
                else
                {
                    LeaveCriticalSection(&g_csRingingCallData);
                }
            }

            // Append the remaining data to the read bytes buffer
            if (cbRemainder && !AppendReadBytes(szRemainder, cbRemainder, fDataOnNotificationComPort))
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : cbRemainder and AppendReadBytes failed\r\n")));
                goto Exit;
            }
        }

        // Free memory occupied by szAppend
        delete[] szAppend;
        szAppend = NULL;
        szRemainder = NULL;
        cbAppend = 0;
    }

    bSuccess=TRUE;
Exit:
    if ( szAppend )
    {
        delete [] szAppend;
        szAppend = NULL;
        cbAppend = 0;
        szRemainder = NULL;
        cbRemainder = 0;
    }
    delete pRsp;
    return bSuccess;
}

#define RESP_DATA_BUF 1024
DWORD CRilHandle::ResponseThread()
{
    FUNCTION_TRACE(CRilHandle::ResponseThread);
    char szData[RESP_DATA_BUF];
    DWORD dwMask;
    DWORD dwRead;
    DWORD dwErrors;
#ifdef OEM2_DRIVER    // use a seperate port for unsoliticated notifications
    char szData2[RESP_DATA_BUF];
    DWORD dwRead2;
#endif  // OEM2_DRIVER

    DEBUGCHK(m_pComDevice != NULL);

    // Tell the main thread that we've reached the checkpoint
    GetCheckPoint()->Reached();

    while (1)
    {
        // See if the thread needs to terminate
        if (FCancelSet())
        {
            break;
        }

        // Wait for more data
        if ( (!m_pComDevice->VirtWaitCommEvent(&dwMask)) || (!dwMask) )
        {
            // Due to the way the comm handle is passed around, we could have a race condition such that the event mask gets reset
            // just before or while we're waiting. To handle this case, we just check to see if the mask was reset out from
            // under us, and if it was, we just try again after resetting to a valid state.
            // This should never happen though
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : VirtWaitCommEvent failed\r\n")));
            DWORD dwCommMask=0;
            if (!m_pComDevice->VirtGetCommMask(&dwCommMask) || (dwCommMask!=DEFAULT_COM_MASK))
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : VirtWaitCommEvent failed, CommMask was 0x%x\r\n"),dwCommMask));
                m_pComDevice->VirtSetCommMask(DEFAULT_COM_MASK);
            }
            continue;
        }

        if (dwMask & EV_ERR)
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : VirtWaitCommEvent returned EV_ERR\r\n")));

            // Error is detected on the downstream port
            if (m_pComDevice->VirtClearCommError(&dwErrors, NULL))
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Detected comm error: %x\r\n"), dwErrors));
            }
        }

        if (dwMask & EV_BREAK)
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread : VirtWaitCommEvent returned EV_BREAK\r\n")));

            // Break is detected on the downstream port
            if (m_pComDevice->VirtClearCommBreak())
            {
                RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Detected comm break\r\n")));
            }
        }

        if (dwMask & EV_RXCHAR)
        {
            // DEBUGMSG(ZONE_INFO, (TEXT("RILDrv : i : CRilHandle::ResponseThread : VirtWaitCommEvent returned EV_RXCHAR\r\n")));

            do
            {
                if (!m_pComDevice->Read(szData, RESP_DATA_BUF, dwRead))
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Read failed\r\n")));
                    break;
                }

                if (!dwRead)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread no data?!\r\n")));
                    break;
                }

#ifdef SIMULATE_HUNG_RADIO
                if (g_fSimulateHungRadio == TRUE)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread Simulating hung radio, discarding data\r\n")));
                    dwRead = 0;
                    break;
                }
#endif

                if (!HandleRxData(szData,dwRead,false))
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread: HandleRxData failed?!\r\n")));
                    break;
                }
            } while (dwRead==RESP_DATA_BUF);
        }

#ifdef OEM2_DRIVER    // use szData2 and dwRead2
        if (dwMask & EV_EVENT2)
        {
            // DEBUGMSG(ZONE_INFO, (TEXT("RILDrv : i : CRilHandle::ResponseThread : VirtWaitCommEvent returned EV_RXCHAR\r\n")));

            do
            {
                if (!m_pComDevice->Read2(szData2, RESP_DATA_BUF, dwRead2))
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread : Read failed\r\n")));
                    break;
                }

                if (!dwRead2)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread no data?!\r\n")));
                    break;
                }

#ifdef SIMULATE_HUNG_RADIO
                if (g_fSimulateHungRadio == TRUE)
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : CRilHandle::ResponseThread Simulating hung radio, discarding data\r\n")));
                    dwRead2 = 0;
                    break;
                }
#endif

                if (!HandleRxData(szData2,dwRead2,true))
                {
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::ResponseThread: HandleRxData failed?!\r\n")));
                    break;
                }
            } while (dwRead2==RESP_DATA_BUF);
        }
#endif // OEM2_DRIVER
    }

    return 0;
}

/**********************************************************************/

ATCmd::ATCmd()
{
    InitializeCriticalSection( &m_cs );
    m_hEvent = NULL;
    m_hr = E_NOTIMPL;
    m_fInProgress = FALSE;
}

ATCmd::~ATCmd()
{
    if ( m_hEvent != NULL )
    {
        CloseHandle( m_hEvent );
        m_hEvent = NULL;
    }
    DeleteCriticalSection( &m_cs );
}

void
ATCmd::Dump() const
{
#ifdef DEBUG
    SYNCBLOCK( m_cs );
    _tprintf(TEXT("event       [%08x]"), m_hEvent);
    _tprintf(TEXT("hr          [%08x]"), m_hr);
    _tprintf(TEXT("in progress [%d]"), (int)m_fInProgress);
#endif
}

HRESULT
ATCmd::Execute( const char *cmd )
{
    HRESULT hr = E_INVALIDARG;

    if ( cmd != NULL )
    {
        SYNCBLOCK( m_cs );

        hr = E_FAIL;

        HRESULT hr2 = Initialize();
        if ( SUCCEEDED( hr2 ) && !m_fInProgress )
        {
            if ( QueueCmdIgnoreRspWithData(APIID_NONE,              // appiid
                                            cmd,                    // szCmd
                                            CMDOPT_FORCEPARSE,      // dwOptions
                                            20000,                  // dwTimeout
                                            NULL,                   // Parse fn
                                            NULL,                   // Ptr to notification data
                                            0,                      // dwRetries
                                            0,                      // dwRetriesOnError
                                            0,                      // dwRetryOnErrorDelay
                                            &ATCmd::s_ParseFunc,    // parse fn with data
                                            this) )                 // Data
            {
                m_fInProgress = TRUE;

                {
                    UNSYNCBLOCK( m_cs );

                    /* wait till the result comes back */
                    DWORD dwWaitResult = WaitForSingleObject( m_hEvent, INFINITE );
                }

                /* return the result of the parsing */
                hr = m_hr;
            }
        }
    }

    return hr;
}

HRESULT
ATCmd::Initialize()
{
    HRESULT hr = E_OUTOFMEMORY;
    {
        SYNCBLOCK( m_cs );

        if ( m_hEvent == NULL )
        {
            m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
        }

        if ( m_hEvent != NULL )
        {
            hr = S_OK;
        }
    }
    return hr;
}

HRESULT
ATCmd::s_ParseFunc( LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData )
{
    HRESULT hr = E_INVALIDARG;

    pBlob  = NULL;
    cbBlob = 0;

    if ( pfnParseData != NULL )
    {
        ATCmd *pATCmd = reinterpret_cast<ATCmd*>( pfnParseData );
        if ( pATCmd != NULL )
        {
            hr = pATCmd->ParseFunc( szRsp );
        }
        else
        {
            hr = E_NOTIMPL;
        }
        pATCmd = NULL;
    }

    return hr;
}

HRESULT
ATCmd::ParseFunc( LPCSTR szRsp )
{
    HRESULT hr = Parse( szRsp );
    {
        SYNCBLOCK( m_cs );

        /* store the result of the parsing */
        m_hr = hr;

        m_fInProgress = FALSE;
    }

    /* notify */
    SetEvent( m_hEvent );

    hr = S_OK;
    return hr;
}
